<?php
/**
 * Copyright (c) 2020
 * 摘    要：
 * 作    者：san
 * 修改日期：2020.04.06
 */

namespace App\Library\Clog;

use Psr\Log\InvalidArgumentException;

class Logger
{
    const DEBUG     = 100;
    const INFO      = 200;
    const NOTICE    = 250;
    const WARNING   = 300;
    const ERROR     = 400;
    const CRITICAL  = 500;
    const ALERT     = 550;
    const EMERGENCY = 600;

    static public $logs = [];

    protected static $levels = array(
        self::DEBUG     => 'debug',
        self::INFO      => 'info',
        self::NOTICE    => 'notice',
        self::WARNING   => 'warning',
        self::ERROR     => 'error',
        self::CRITICAL  => 'critical',
        self::ALERT     => 'alert',
        self::EMERGENCY => 'emergency',
    );

    static protected $config;

    static protected $defDriver = [
        'driver'   => 'file',
        'filename' => 'debug'
    ];

    static protected $baseRecord = [];

    protected $name;

    /**
     * Logger constructor.
     *
     * @param $name
     */
    public function __construct($name)
    {
        $this->name = $name;
        if (!static::$baseRecord) {
            static::$baseRecord = [
                'hostname' => gethostname(),
                'pid'      => getmypid(),
                'app_name' => env('APP_NAME'),
            ];
        }
    }

    public function useDailyFiles(...$n)
    {
        return true;
    }

    /**
     * debug
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function debug($message, array $context = array())
    {
        return $this->addRecord(static::DEBUG, $message, $context);
    }

    /**
     * info
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function info($message, array $context = array())
    {
        return $this->addRecord(static::INFO, $message, $context);
    }

    /**
     * notice
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function notice($message, array $context = array())
    {
        return $this->addRecord(static::NOTICE, $message, $context);
    }

    /**
     * warn
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function warn($message, array $context = array())
    {
        return $this->addRecord(static::WARNING, $message, $context);
    }

    /**
     * warning
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function warning($message, array $context = array())
    {
        return $this->addRecord(static::WARNING, $message, $context);
    }

    /**
     * err
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function err($message, array $context = array())
    {
        return $this->addRecord(static::ERROR, $message, $context);
    }

    /**
     * error
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function error($message, array $context = array())
    {
        return $this->addRecord(static::ERROR, $message, $context);
    }

    /**
     * crit
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function crit($message, array $context = array())
    {
        return $this->addRecord(static::CRITICAL, $message, $context);
    }

    /**
     * critical
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function critical($message, array $context = array())
    {
        return $this->addRecord(static::CRITICAL, $message, $context);
    }

    /**
     * alert
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function alert($message, array $context = array())
    {
        return $this->addRecord(static::ALERT, $message, $context);
    }

    /**
     * emerg
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function emerg($message, array $context = array())
    {
        return $this->addRecord(static::EMERGENCY, $message, $context);
    }

    /**
     * emergency
     *
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function emergency($message, array $context = array())
    {
        return $this->addRecord(static::EMERGENCY, $message, $context);
    }

    /**
     * log Instance
     *
     * @param string $level
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    public function log($level, $message, array $context = array())
    {
        $level = static::toMonologLevel($level);

        return $this->addRecord($level, $message, $context);
    }

    /**
     * addRecord
     *
     * @param string $level
     * @param string $message
     * @param array $context
     *
     * @return mixed
     */
    protected function addRecord($level, $message, $context = [])
    {
        if (is_string($context)) {
            $context = [$context];
        }

        $levelName            = static::getLevelName($level);
        $record               = static::$baseRecord;
        $record['message']    = (string)$message;
        $record['context']    = $context;
        $record['level']      = $level;
        $record['level_name'] = $levelName;
        $record['channel']    = $this->name;
        $record['datetime']   = date("Y-m-d H:i:s");

        self::file($this->name, $record);
        return true;
    }


    /**
     * file storage
     *
     * @param $name
     * @param $record
     *
     * @return mixed
     */
    public static function file($name, $record)
    {
        if (!isset(static::$logs[$name])) {
            $filename = $name ?? static::$defFilename;
            if (strpos($filename, "\\") !== false ||
                strpos($filename, "Class") !== false ||
                strpos($filename, "Call to") !== false) {
                $filename = 'error';
            }
            $filename            = BASE_PATH . '/runtime/logs/' . $filename . ".log";
            static::$logs[$name] = static::getTimedFilename($filename);
        }

        return file_put_contents(static::$logs[$name], json_encode($record, JSON_UNESCAPED_UNICODE) . PHP_EOL, FILE_APPEND);
    }

    /**
     * @param        $filename
     * @param string $filenameFormat
     * @param string $dateFormat
     *
     * @return mixed
     */
    public static function getTimedFilename($filename, $filenameFormat = '{filename}-{date}', $dateFormat = 'Y-m-d')
    {
        $fileInfo      = pathinfo($filename);
        $timedFilename = str_replace(
            array('{filename}', '{date}'),
            array($fileInfo['filename'], date($dateFormat)),
            $fileInfo['dirname'] . '/' . $filenameFormat
        );

        if (!empty($fileInfo['extension'])) {
            $timedFilename .= '.' . $fileInfo['extension'];
        }

        return $timedFilename;
    }

    /**
     * @param $level
     *
     * @return mixed
     */
    public static function getLevelName($level)
    {
        if (!isset(static::$levels[$level])) {
            throw new InvalidArgumentException('Level "' . $level . '" is not defined, use one of: ' . implode(', ', array_keys(static::$levels)));
        }

        return static::$levels[$level];
    }


    public static function toMonologLevel($level)
    {
        if (is_string($level) && defined(__CLASS__ . '::' . strtoupper($level))) {
            return constant(__CLASS__ . '::' . strtoupper($level));
        }

        return $level;
    }

}
